﻿using System;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using Microsoft.Devices;

using CoreSharp.Keyboard;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework;
using Microsoft.Phone.Shell;
using System.Collections.Generic;

using CoreSharp.Wp7;

namespace CoreSharp.Controls
{

    [TemplatePart(Name="PART_Cursor", Type=typeof(FrameworkElement))]
    [TemplateVisualState(GroupName="CursorStates", Name="ShowCursor" )]
    [TemplateVisualState(GroupName = "CursorStates", Name = "HideCursor")]
    public class ThaiTextBox : TextBox
    {
        private CompositeTransform _CursorPos;

        private Keyboard _Keyboard;
        public Keyboard Keyboard
        {
            set
            {
                if (_Keyboard != null)
                    throw new InvalidOperationException("Cannot set Keyboard more than once");

                _Keyboard = value;

                _Keyboard.DataContext = CoreSharp.Keyboard.Layouts.ThaiKeyboardLayout.Default;

                _Keyboard.KeyInput += new Action<string>(Keyboard_KeyInput);
                _Keyboard.KeyDelete += new EventHandler(Keyboard_KeyDelete);
                _Keyboard.RequestEnKeyboard += delegate
                {
                    this.Focus();
                };

            }
        }

        public ThaiTextBoxSettings Settings
        {
            get { return GlobalThaiTextBoxSettings.Global; }
        }

        /// <summary>
        /// Automatically binds to application bar buttons (expects 4 buttons shift,more,space,bksp)
        /// </summary>
        public ApplicationBarIconButton[] ApplicationBarButtons
        {
            set
            {
                if (value.Length != 4)
                    throw new InvalidOperationException("ApplicationBar must contains 4 buttons which is shift, more, space and backspace in order.");

                if (_Keyboard == null)
                    throw new InvalidOperationException("Keyboard was not set");

                value[0].Click += delegate
                {
                    _Keyboard.ToggleShift();
                };
                value[1].Click += delegate
                {
                    _Keyboard.ToggleAlt();
                };
                value[2].Click += delegate
                {
                    this.SendKey(' ');
                };
                value[3].Click += delegate
                {
                    this.Delete();
                };
            }
        }

        private SoundEffect _ValidKeySound;
        private SoundEffect ValidKeySound
        {
            get
            {
                if (_ValidKeySound == null)
                {
                    _ValidKeySound = SoundEffect.FromStream(TitleContainer.OpenStream("sounds/type.wav"));
                }
                return _ValidKeySound;
            }
        }

        private SoundEffect _InvalidKeySound;
        private SoundEffect InvalidKeySound
        {
            get
            {
                if (_InvalidKeySound == null)
                {
                    _InvalidKeySound = SoundEffect.FromStream(TitleContainer.OpenStream("sounds/cant.wav"));
                }
                return _InvalidKeySound;
            }
        }

        public event Action InvalidKey;
        public event Action ValidKey;
        public event Action EnterKeyPressed;

        public ThaiTextBox()
        {
            this.DefaultStyleKey = typeof(ThaiTextBox);

            this.TextChanged += delegate
            {
                this.UpdateCursor();
            };
        }

        public void SendKey(char c)
        {
            this.Keyboard_KeyInput(c.ToString());
        }

        public void Delete()
        {
            this.Keyboard_KeyDelete(null, EventArgs.Empty);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            var cursor = this.GetTemplateChild("PART_Cursor") as FrameworkElement;
            if (cursor != null)
            {
                if (cursor.RenderTransform == null)
                    cursor.RenderTransform = new CompositeTransform();

                _CursorPos = cursor.RenderTransform as CompositeTransform;
            }

            VisualStateManager.GoToState(this, "ShowCursor", false);
        }

        protected override void OnGotFocus(RoutedEventArgs e)
        {
            VisualStateManager.GoToState(this, "HideCursor", false);
            base.OnGotFocus(e);
        }

        protected override void OnLostFocus(RoutedEventArgs e)
        {
            this.UpdateCursor();
            this.SelectionLength = 0;
            VisualStateManager.GoToState(this, "ShowCursor", false);
            base.OnLostFocus(e);
        }

        protected void OnInvalidKey()
        {
            if (this.Settings.PlayInvalidKeySound)
            {
                FrameworkDispatcher.Update();
                this.InvalidKeySound.Play();
            }

            if (this.Settings.VibrateOnInvalidKey)
            {
                Microsoft.Devices.VibrateController.Default.Start(TimeSpan.FromMilliseconds(18));

                (new Action( ()=>{
                    Microsoft.Devices.VibrateController.Default.Start(TimeSpan.FromMilliseconds(10));
                })).Delay(20);
            }

            if (this.InvalidKey != null)
                this.InvalidKey();
        }

        protected void OnValidKey()
        {
            if (this.Settings.PlayValidKeySound)
            {
                FrameworkDispatcher.Update();
                this.ValidKeySound.Play();
            }

            if (this.Settings.VibrateOnValidKey)
            {
                Microsoft.Devices.VibrateController.Default.Start(TimeSpan.FromMilliseconds(18));

                (new Action(() =>
                {
                    Microsoft.Devices.VibrateController.Default.Start(TimeSpan.FromMilliseconds(10));
                })).Delay(20);
            }

            if (this.ValidKey != null)
                this.ValidKey();
        }

        protected void OnEnterKeyPress()
        {
            if (this.EnterKeyPressed != null)
                this.EnterKeyPressed();
        }

        private void UpdateCursor()
        {
            if (_CursorPos == null)
                return;

            if (this.SelectionStart == 0)
                this.SelectionStart = this.Text.Length;

            // still 0
            if (this.SelectionStart == 0)
            {
                _CursorPos.TranslateX = 0;
                _CursorPos.TranslateY = 0;
                return;
            }

            int offset = 1;
            if (this.Text.Length > 0)
            {
                // Thai Hack - WP7 seems to give wrong positions for these characters
                // put in loop because some of it can share same base char

                string hack = "ุูึัํี๊้็่๋ิฺื์";
                while (this.SelectionStart - offset > 0 && hack.Contains(this.Text[this.SelectionStart - offset]))
                {
                    offset++;
                }
            }

            var rect =
                this.GetRectFromCharacterIndex(this.SelectionStart - offset);

            _CursorPos.TranslateX = rect.Left;
            _CursorPos.TranslateY = rect.Top - 10;
        }

        protected override void OnKeyUp(System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Enter)
            {
                this.OnEnterKeyPress();
            }
            base.OnKeyUp(e);
        }

        private void Keyboard_KeyDelete(object sender, EventArgs e)
        {
            if (this.Text.Length == 0)
            {
                this.OnInvalidKey();
                return;
            }

            if (this.SelectionStart > 0 && this.SelectionStart != this.Text.Length)
            {
                string before = this.Text.Substring(0, this.SelectionStart - 1);
                string after = this.Text.Substring(this.SelectionStart);

                this.Text = before + after;
                this.SelectionStart = before.Length;
            }
            else
            {
                this.Text = this.Text.Substring(0, this.Text.Length - 1);
            }

            this.OnValidKey();
        }

        private void Keyboard_KeyInput(string obj)
        {
            var oldSelection = this.SelectionStart;

            if (this.SelectionStart > 0 && this.SelectionStart != this.Text.Length)
            {
                if (this.CheckSequence(this.Text[this.SelectionStart - 1], obj[0]))
                {
                    this.Text = this.Text.Insert(this.SelectionStart, obj);
                    this.SelectionStart = oldSelection + 1;
                    this.OnValidKey();
                }
                else
                {
                    this.OnInvalidKey();
                }
            }
            else
            {
                bool pass = true;
                if (this.Text.Length == 0)
                {
                    pass = this.CheckSequence('-', obj[0]);
                }
                else
                {
                    pass = this.CheckSequence(this.Text[this.Text.Length - 1], obj[0]);
                }

                if (pass)
                {
                    this.Text += obj;
                    this.OnValidKey();
                }
                else
                {
                    this.OnInvalidKey();
                }
            }

        }

        private char[] characters = "ภถคตจขชฎพฑธรณนยญบฐลฟฤหฆกฏดฌษสศวซงผปฉอฮทมฒฬฝฦ".ToCharArray();
        private char[] วรรณยุกต์ = "่็๊๋".ToCharArray();
        private char[] สระทั่วไป = "ๆไะฯโเา".ToCharArray();
        private char[] สระนำด้วยอักษร = "ุูึำัํี็ิฺื์".ToCharArray();

        protected virtual bool CheckSequence(char last, char inserted)
        {
            if (สระนำด้วยอักษร.Contains(inserted)) // inserting สระนำด้วยอักษร
            {
                // make sure we have leading characters and not double สระนำด้วยอักษร and does not write over วรรณยุกต์
                return characters.Contains(last) && !สระนำด้วยอักษร.Contains(last) && !วรรณยุกต์.Contains(last);
            }

            if (วรรณยุกต์.Contains(inserted)) // inserting วรรณยุกต์
            {
                if (last == '-') // beginning
                    return false;

                // we only add to thai chars, don't place it over สระทั่วไป and make sure we don't double วรรณยุกต์
                if (characters.Contains(last))
                    return true;

                return !สระทั่วไป.Contains(last) && !วรรณยุกต์.Contains(last);
            }

            return true; // nothing more to check
        }

    }
}
